import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os, re, base64, mimetypes, threading, subprocess, sys
from io import BytesIO
from PIL import Image
import requests
import markdown

def compress_and_encode_image(source, is_url=False, quality=80):
    try:
        if is_url:
            response = requests.get(source, timeout=5)
            response.raise_for_status()
            mime_type = response.headers.get("Content-Type", "image/jpeg")
            image_data = BytesIO(response.content)
        else:
            mime_type, _ = mimetypes.guess_type(source)
            image_data = open(source, "rb")

        img = Image.open(image_data)
        buffer = BytesIO()
        img.save(buffer, format=img.format if mime_type != 'image/jpeg' else 'JPEG', quality=quality, optimize=True)
        return f"data:{mime_type};base64," + base64.b64encode(buffer.getvalue()).decode("utf-8")
    except Exception as e:
        print(f"⚠️ 图片处理失败: {source} - {e}")
        return None
    finally:
        if not is_url and 'image_data' in locals():
            image_data.close()

def process_markdown(file_path, overwrite=False, quality=80, compress=True):
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()

    pattern = r'!\[([^\]]*)\]\(([^)]+)\)'
    matches = list(re.finditer(pattern, content))
    refs = []
    replaced = content
    used_refs = set()
    total_base64_bytes = 0

    for i, match in enumerate(matches):
        img_path = match.group(2).strip()
        is_url = img_path.lower().startswith("http")
        full_path = img_path if is_url else os.path.join(os.path.dirname(file_path), img_path)

        base64_str = compress_and_encode_image(full_path, is_url=is_url, quality=quality) if compress else None
        if not base64_str:
            continue

        ref_name = f"img_{i+1}"
        while ref_name in used_refs:
            i += 1
            ref_name = f"img_{i+1}"
        used_refs.add(ref_name)

        replaced = replaced.replace(match.group(0), f"![{ref_name}][{ref_name}]")
        refs.append(f"[{ref_name}]: {base64_str}")
        total_base64_bytes += len(base64_str.encode("utf-8"))

    if refs:
        replaced += "\n\n<!-- Base64 引用（用于图片显示） -->\n" + "\n".join(refs) + "\n"
        replaced += "\n<details><summary>📦 查看 Base64 原始数据</summary>\n\n```text\n"
        replaced += "\n".join(refs)
        replaced += "\n```\n</details>\n"

    output_path = file_path if overwrite else file_path.replace(".md", "_with_base64.md")
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(replaced)

    return output_path, total_base64_bytes

def export_html_from_markdown(md_path):
    html_output_path = md_path.replace(".md", ".html")
    try:
        with open(md_path, "r", encoding="utf-8") as f:
            md_content = f.read()

        html_body = markdown.markdown(md_content, extensions=["extra", "tables"])
        html_template = f"""
<!DOCTYPE html>
<html lang=\"zh-CN\">
<head>
    <meta charset=\"UTF-8\">
    <title>{os.path.basename(md_path)}</title>
    <style>
        body {{ font-family: Arial, sans-serif; padding: 20px; max-width: 900px; margin: auto; background: #fff; }}
        img {{ max-width: 100%; height: auto; cursor: zoom-in; transition: transform 0.2s ease; }}
        #img-modal {{
            display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0,0,0,0.85); justify-content: center; align-items: center; z-index: 9999;
        }}
        #img-modal img {{ max-width: 90%; max-height: 90%; transition: transform 0.2s ease; }}
    </style>
</head>
<body>
{html_body}
<div id=\"img-modal\" onclick=\"hideModal()\">
    <img id=\"modal-img\" src=\"\">
</div>
<script>
    let currentScale = 1;
    const modal = document.getElementById("img-modal");
    const modalImg = document.getElementById("modal-img");

    function showModal(src) {{
        modal.style.display = "flex";
        modalImg.src = src;
        currentScale = 1;
        modalImg.style.transform = "scale(1)";
    }}

    function hideModal() {{
        modal.style.display = "none";
        modalImg.src = "";
    }}

    function zoomImage(event) {{
        event.preventDefault();
        const delta = Math.sign(event.deltaY);
        const scaleStep = 0.1;
        if (delta < 0) {{ currentScale += scaleStep; }}
        else {{ currentScale = Math.max(0.1, currentScale - scaleStep); }}
        modalImg.style.transform = "scale(" + currentScale + ")";
    }}

    document.querySelectorAll("img").forEach(img => {{
        img.addEventListener("click", e => {{
            e.stopPropagation();
            showModal(img.src);
        }});
    }});
    modalImg.addEventListener("wheel", zoomImage);
</script>
</body>
</html>
"""
        with open(html_output_path, "w", encoding="utf-8") as f:
            f.write(html_template)
        return html_output_path
    except Exception as e:
        print(f"❌ 导出 HTML 失败: {e}")
        return None

def threaded_conversion():
    button_convert.config(state='disabled')
    threading.Thread(target=start_conversion).start()

def start_conversion():
    path = entry_path.get().strip()
    if not os.path.exists(path):
        messagebox.showerror("错误", "请选择有效的 Markdown 文件或文件夹。")
        button_convert.config(state='normal')
        return

    try:
        quality = int(entry_quality.get())
    except ValueError:
        messagebox.showerror("错误", "请输入有效的 JPEG 压缩质量。")
        button_convert.config(state='normal')
        return

    overwrite = var_overwrite.get()
    compress = var_compress.get()
    export_html = var_export_html.get()

    files = [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.md')] if os.path.isdir(path) else [path]
    total = len(files)
    if total == 0:
        messagebox.showinfo("提示", "没有找到 Markdown 文件。")
        button_convert.config(state='normal')
        return

    success_count = 0
    total_base64_bytes = 0
    last_output_paths = []

    for index, file in enumerate(files):
        out_path, file_base64_bytes = process_markdown(file, overwrite=overwrite, quality=quality, compress=compress)
        if out_path:
            success_count += 1
            total_base64_bytes += file_base64_bytes
            last_output_paths.append(out_path)
            if export_html:
                export_html_from_markdown(out_path)

        progress = int((index + 1) / total * 100)
        progress_var.set(progress)
        root.update_idletasks()

    button_convert.config(state='normal')
    progress_var.set(0)

    base64_mb = total_base64_bytes / (1024 * 1024)
    messagebox.showinfo("完成", f"✔️ 已处理 {success_count} 个 Markdown 文件。\n📦 Base64 总大小：{base64_mb:.2f} MB")

    if last_output_paths:
        folder = os.path.dirname(last_output_paths[-1])
        try:
            if sys.platform == "win32":
                os.startfile(folder)
            elif sys.platform == "darwin":
                subprocess.run(["open", folder])
            else:
                subprocess.run(["xdg-open", folder])
        except Exception as e:
            messagebox.showwarning("警告", f"无法打开文件夹：{e}")

def browse_file():
    p = filedialog.askopenfilename(filetypes=[("Markdown 文件", "*.md")])
    if p: entry_path.delete(0, tk.END); entry_path.insert(0, p)

def browse_folder():
    p = filedialog.askdirectory()
    if p: entry_path.delete(0, tk.END); entry_path.insert(0, p)

root = tk.Tk()
root.title("📄 Markdown 图片转 Base64 工具 v1.0")
frame = ttk.Frame(root, padding=10)
frame.grid(row=0, column=0)

entry_path = ttk.Entry(frame, width=50)
entry_path.grid(row=0, column=0, columnspan=2, pady=5)
ttk.Button(frame, text="选择文件", command=browse_file).grid(row=0, column=2)
ttk.Button(frame, text="选择文件夹", command=browse_folder).grid(row=0, column=3)

ttk.Label(frame, text="JPEG 质量（1-95）：").grid(row=1, column=0, sticky="e")
entry_quality = ttk.Entry(frame, width=10)
entry_quality.insert(0, "80")
entry_quality.grid(row=1, column=1, sticky="w")

var_compress = tk.BooleanVar(value=False)
ttk.Checkbutton(frame, text="自动压缩图片", variable=var_compress).grid(row=2, column=0, columnspan=4, sticky="w")

var_overwrite = tk.BooleanVar(value=False)
ttk.Checkbutton(frame, text="覆盖原文件", variable=var_overwrite).grid(row=3, column=0, columnspan=4, sticky="w")

var_export_html = tk.BooleanVar(value=True)
ttk.Checkbutton(frame, text="导出 HTML 文件", variable=var_export_html).grid(row=4, column=0, columnspan=4, sticky="w")

button_convert = ttk.Button(frame, text="开始转换", command=threaded_conversion)
button_convert.grid(row=5, column=0, pady=10)
ttk.Button(frame, text="退出", command=root.quit).grid(row=5, column=1, pady=10)

progress_var = tk.DoubleVar()
ttk.Progressbar(frame, variable=progress_var, maximum=100).grid(row=6, column=0, columnspan=4, sticky="we", pady=5)

root.mainloop()
